// 版权所有2012 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// Objdump反汇编可执行文件。
// 
// 用法：
// 
// go tool objdump[-s symregexp]binary 
// 
// objdump打印二进制中所有文本符号（代码）的反汇编。
// 如果存在-s选项，objdump只反汇编名称与正则表达式匹配的符号。
// 
// 替代用法：
// 
// go tool objdump binary start end 
// 
// 在此模式下，objdump反汇编从起始地址开始的二进制文件，
// 停止在结束地址。开始和结束地址是程序
// 计数器，用十六进制写成，前导0x前缀可选。
// 在这种模式下，objdump打印一系列小节，格式如下：
// 
// 文件：行
// 地址：assembly 
// 地址：assembly 
// /。。。
// 
// 每一节都对连续的地址范围进行反汇编
// 所有地址都映射到相同的原始源文件和行号。
// 此模式供pprof使用。
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"

	"cmd/internal/objfile"
)

var printCode = flag.Bool("S", false, "print Go code alongside assembly")
var symregexp = flag.String("s", "", "only dump symbols matching this regexp")
var gnuAsm = flag.Bool("gnu", false, "print GNU assembly next to Go assembly (where supported)")
var symRE *regexp.Regexp

func usage() {
	fmt.Fprintf(os.Stderr, "usage: go tool objdump [-S] [-gnu] [-s symregexp] binary [start end]\n\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	log.SetFlags(0)
	log.SetPrefix("objdump: ")

	flag.Usage = usage
	flag.Parse()
	if flag.NArg() != 1 && flag.NArg() != 3 {
		usage()
	}

	if *symregexp != "" {
		re, err := regexp.Compile(*symregexp)
		if err != nil {
			log.Fatalf("invalid -s regexp: %v", err)
		}
		symRE = re
	}

	f, err := objfile.Open(flag.Arg(0))
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	dis, err := f.Disasm()
	if err != nil {
		log.Fatalf("disassemble %s: %v", flag.Arg(0), err)
	}

	switch flag.NArg() {
	default:
		usage()
	case 1:
		// 分解整个对象
		dis.Print(os.Stdout, symRE, 0, ^uint64(0), *printCode, *gnuAsm)

	case 3:
		// 分解PC系列
		start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)
		if err != nil {
			log.Fatalf("invalid start PC: %v", err)
		}
		end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64)
		if err != nil {
			log.Fatalf("invalid end PC: %v", err)
		}
		dis.Print(os.Stdout, symRE, start, end, *printCode, *gnuAsm)
	}
}
